#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
剑网3装备数据MCP服务器 - 优化版本

集成缓存、配置管理、监控统计等高级功能
为AI模型提供高效的剑网3游戏数据查询服务
"""

from fastapi import FastAPI, Query, HTTPException, Path, Request, Depends
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from fastapi_mcp import FastApiMCP
import httpx
import asyncio
import time
from typing import Optional, Literal, Union, Dict, Any, List
from pydantic import BaseModel, Field
from contextlib import asynccontextmanager

# 导入自定义模块
from config import server_config, api_endpoints, response_config
from cache_manager import cache_manager, cached, start_cache_cleanup_task


# 应用启动和关闭事件处理
@asynccontextmanager
async def lifespan(app: FastAPI):
    """应用生命周期管理"""
    print("🚀 正在启动剑网3装备数据MCP服务器...")
    
    # 启动时执行
    if server_config.enable_cache:
        await start_cache_cleanup_task()
        print("✅ 缓存系统已启动")
    
    print(f"🌐 服务器地址: http://{server_config.host}:{server_config.port}")
    print(f"📚 API文档: http://{server_config.host}:{server_config.port}/docs")
    print("🎮 MCP工具已就绪，可供AI模型调用！")
    
    yield
    
    # 关闭时执行
    print("🛑 正在关闭MCP服务器...")
    if cache_manager:
        await cache_manager.clear()
    print("✅ 服务器已安全关闭")


# 创建FastAPI应用实例
app = FastAPI(
    title=server_config.title,
    description=server_config.description,
    version=server_config.version,
    lifespan=lifespan,
    docs_url="/docs",
    redoc_url="/redoc"
)

# 添加CORS中间件
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 创建MCP服务器实例
mcp = FastApiMCP(
    app,
    name=server_config.mcp_name,
    description=server_config.mcp_description
)
mcp.mount()

# HTTP客户端单例
http_client: Optional[httpx.AsyncClient] = None

async def get_http_client() -> httpx.AsyncClient:
    """获取HTTP客户端单例"""
    global http_client
    if http_client is None:
        http_client = httpx.AsyncClient(
            timeout=server_config.request_timeout,
            limits=httpx.Limits(max_connections=50, max_keepalive_connections=20)
        )
    return http_client


# 响应数据模型定义
class BaseResponse(BaseModel):
    """基础响应模型"""
    success: bool = True
    message: str = "操作成功"
    timestamp: float = Field(default_factory=time.time)
    
    class Config:
        extra = "allow"


class EquipmentResponse(BaseResponse):
    """装备查询响应模型"""
    total: int
    per: int
    pages: int
    page: int
    list: List[Dict[str, Any]]
    
    class Config:
        extra = "allow"


class SetResponse(BaseResponse):
    """套装查询响应模型"""
    info: Dict[str, Any]
    list: List[Dict[str, Any]]
    
    class Config:
        extra = "allow"


class SingleEquipResponse(BaseResponse):
    """单个装备详情响应模型"""
    id: int
    Name: str
    Level: int
    BelongSchool: Optional[str]
    MagicKind: Optional[str]
    MagicType: Optional[str]
    GetType: Optional[str]
    
    class Config:
        extra = "allow"


class StatsResponse(BaseResponse):
    """统计信息响应模型"""
    cache_stats: Optional[Dict[str, Any]] = None
    server_stats: Dict[str, Any]
    config_info: Dict[str, Any]


class SchoolEquipmentSummary(BaseResponse):
    """门派装备汇总响应模型"""
    school_name: str
    total_equipment: int
    equipment_by_type: Dict[str, int]
    recommended_sets: List[Dict[str, Any]]


# 请求计数器
request_counter = {"total": 0, "by_endpoint": {}}

def count_request(endpoint: str):
    """记录请求统计"""
    request_counter["total"] += 1
    request_counter["by_endpoint"][endpoint] = request_counter["by_endpoint"].get(endpoint, 0) + 1


# 核心API端点

@app.get("/equipment/search", operation_id="search_equipment")
@cached("equipment_search", ttl=server_config.cache_ttl)
async def search_equipment(
    equipment_type: Literal["weapon", "armor", "trinket"] = Query(
        description="装备类型：weapon(武器)、armor(装备)、trinket(饰品)"
    ),
    position: Optional[int] = Query(
        None, 
        description="位置类型：0主武器，1暗器（仅weapon类型有效）"
    ),
    pv_type: Optional[int] = Query(
        None, 
        description="PV类型：1PVE，2PVP，3PVX"
    ),
    duty: Optional[int] = Query(
        None, 
        description="心法类型：1内功，2外功，3坦克，4奶妈"
    ),
    equipment_id: Optional[int] = Query(
        None, 
        alias="id", 
        description="装备ID"
    ),
    recommend_id: Optional[int] = Query(
        None, 
        alias="RecommendID", 
        description="装备推荐ID"
    ),
    quality: Optional[int] = Query(
        None, 
        alias="Quality", 
        description="装备质量"
    ),
    attr: Optional[str] = Query(
        None, 
        description="属性名称"
    ),
    belong_school: Optional[str] = Query(
        None, 
        alias="BelongSchool", 
        description="所属门派"
    ),
    page: int = Query(1, description="页码"),
    per_page: int = Query(10, description="每页数量", le=response_config.max_page_size)
) -> EquipmentResponse:
    """
    查询游戏装备数据
    
    根据不同的筛选条件查询武器、装备或饰品数据
    支持按ID、门派、属性等多种条件进行筛选
    """
    count_request("search_equipment")
    
    # 构建请求URL
    base_url = f"{server_config.jx3box_base_url}/equip/{equipment_type}"
    
    # 构建查询参数
    params = {}
    if position is not None:
        params["position"] = position
    if pv_type is not None:
        params["pv_type"] = pv_type
    if duty is not None:
        params["duty"] = duty
    if equipment_id is not None:
        params["id"] = equipment_id
    if recommend_id is not None:
        params["RecommendID"] = recommend_id
    if quality is not None:
        params["Quality"] = quality
    if attr is not None:
        params["attr"] = attr
    if belong_school is not None:
        params["BelongSchool"] = belong_school
    if page > 1:
        params["page"] = page
    if per_page != response_config.default_page_size:
        params["per"] = per_page
    
    try:
        # 发送HTTP请求到游戏API
        client = await get_http_client()
        response = await client.get(base_url, params=params)
        response.raise_for_status()
        data = response.json()
        
        return EquipmentResponse(**data)
        
    except httpx.HTTPError as e:
        raise HTTPException(
            status_code=500, 
            detail=f"请求游戏API失败: {str(e)}"
        )
    except Exception as e:
        raise HTTPException(
            status_code=500, 
            detail=f"处理请求时发生错误: {str(e)}"
        )


@app.get("/equipment/set/{set_id}", operation_id="get_equipment_set")
@cached("equipment_set", ttl=server_config.cache_ttl)
async def get_equipment_set(
    set_id: int = Path(description="套装ID")
) -> SetResponse:
    """
    根据套装ID查询套装信息
    
    返回套装的基本信息和包含的所有装备列表
    """
    count_request("get_equipment_set")
    
    # 构建请求URL
    url = f"{server_config.jx3box_base_url}/equip/set/{set_id}"
    
    try:
        # 发送HTTP请求到游戏API
        client = await get_http_client()
        response = await client.get(url)
        response.raise_for_status()
        data = response.json()
        
        return SetResponse(**data)
        
    except httpx.HTTPError as e:
        raise HTTPException(
            status_code=500, 
            detail=f"请求游戏API失败: {str(e)}"
        )
    except Exception as e:
        raise HTTPException(
            status_code=500, 
            detail=f"处理请求时发生错误: {str(e)}"
        )


@app.get("/equipment/detail/{tab_type}/{equipment_id}", operation_id="get_equipment_detail")
@cached("equipment_detail", ttl=server_config.cache_ttl)
async def get_equipment_detail(
    tab_type: int = Path(description="装备类型编号"),
    equipment_id: int = Path(description="装备ID")
) -> SingleEquipResponse:
    """
    查询单个装备的详细信息
    
    根据装备类型编号和装备ID获取装备的详细属性信息
    """
    count_request("get_equipment_detail")
    
    # 构建请求URL
    url = f"{server_config.jx3box_base_url}/equip/{tab_type}/{equipment_id}"
    
    try:
        # 发送HTTP请求到游戏API
        client = await get_http_client()
        response = await client.get(url)
        response.raise_for_status()
        data = response.json()
        
        return SingleEquipResponse(**data)
        
    except httpx.HTTPError as e:
        raise HTTPException(
            status_code=500, 
            detail=f"请求游戏API失败: {str(e)}"
        )
    except Exception as e:
        raise HTTPException(
            status_code=500, 
            detail=f"处理请求时发生错误: {str(e)}"
        )


@app.get("/equipment/school/{school_name}", operation_id="get_equipment_by_school")
@cached("equipment_by_school", ttl=server_config.cache_ttl)
async def get_equipment_by_school(
    school_name: str = Path(description="门派名称，如：七秀、天策、纯阳等"),
    equipment_type: Literal["weapon", "armor", "trinket"] = Query(
        "armor", 
        description="装备类型：weapon(武器)、armor(装备)、trinket(饰品)"
    ),
    pv_type: Optional[int] = Query(
        1, 
        description="PV类型：1PVE，2PVP，3PVX"
    )
) -> EquipmentResponse:
    """
    根据门派查询推荐装备
    
    查询特定门派的推荐装备列表，支持按PV类型筛选
    """
    count_request("get_equipment_by_school")
    
    # 构建请求URL和参数
    base_url = f"{server_config.jx3box_base_url}/equip/{equipment_type}"
    params = {
        "BelongSchool": school_name,
        "pv_type": pv_type
    }
    
    try:
        # 发送HTTP请求到游戏API
        client = await get_http_client()
        response = await client.get(base_url, params=params)
        response.raise_for_status()
        data = response.json()
        
        return EquipmentResponse(**data)
        
    except httpx.HTTPError as e:
        raise HTTPException(
            status_code=500, 
            detail=f"请求游戏API失败: {str(e)}"
        )
    except Exception as e:
        raise HTTPException(
            status_code=500, 
            detail=f"处理请求时发生错误: {str(e)}"
        )


# 新增功能API端点

@app.get("/equipment/school/{school_name}/summary", operation_id="get_school_equipment_summary")
@cached("school_summary", ttl=server_config.cache_ttl * 2)  # 更长缓存时间
async def get_school_equipment_summary(
    school_name: str = Path(description="门派名称"),
    pv_type: int = Query(1, description="PV类型：1PVE，2PVP，3PVX")
) -> SchoolEquipmentSummary:
    """
    获取门派装备汇总信息
    
    提供指定门派的装备统计概览，包括各类型装备数量和推荐套装
    """
    count_request("get_school_equipment_summary")
    
    try:
        client = await get_http_client()
        equipment_counts = {}
        recommended_sets = []
        total_count = 0
        
        # 并发查询各种装备类型
        tasks = []
        for eq_type in ["weapon", "armor", "trinket"]:
            task = client.get(
                f"{server_config.jx3box_base_url}/equip/{eq_type}",
                params={"BelongSchool": school_name, "pv_type": pv_type, "per": 1}
            )
            tasks.append((eq_type, task))
        
        # 等待所有请求完成
        for eq_type, task in tasks:
            try:
                response = await task
                if response.status_code == 200:
                    data = response.json()
                    count = data.get("total", 0)
                    equipment_counts[eq_type] = count
                    total_count += count
                else:
                    equipment_counts[eq_type] = 0
            except Exception:
                equipment_counts[eq_type] = 0
        
        return SchoolEquipmentSummary(
            school_name=school_name,
            total_equipment=total_count,
            equipment_by_type=equipment_counts,
            recommended_sets=recommended_sets
        )
        
    except Exception as e:
        raise HTTPException(
            status_code=500,
            detail=f"获取门派装备汇总失败: {str(e)}"
        )


@app.get("/equipment/popular", operation_id="get_popular_equipment")
@cached("popular_equipment", ttl=server_config.cache_ttl * 3)  # 更长缓存
async def get_popular_equipment(
    equipment_type: Literal["weapon", "armor", "trinket"] = Query("armor"),
    limit: int = Query(20, description="返回数量限制", le=50)
) -> EquipmentResponse:
    """
    获取热门装备列表
    
    基于推荐ID和质量筛选出当前热门的装备
    """
    count_request("get_popular_equipment")
    
    try:
        client = await get_http_client()
        # 查询高品质装备
        response = await client.get(
            f"{server_config.jx3box_base_url}/equip/{equipment_type}",
            params={"Quality": 4, "pv_type": 1, "per": limit}
        )
        response.raise_for_status()
        data = response.json()
        
        return EquipmentResponse(**data)
        
    except Exception as e:
        raise HTTPException(
            status_code=500,
            detail=f"获取热门装备失败: {str(e)}"
        )


@app.get("/stats", operation_id="get_server_stats")
async def get_server_stats() -> StatsResponse:
    """
    获取服务器统计信息
    
    包括缓存命中率、请求统计、配置信息等
    """
    server_stats = {
        "uptime": time.time(),
        "total_requests": request_counter["total"],
        "requests_by_endpoint": request_counter["by_endpoint"],
        "cache_enabled": server_config.enable_cache
    }
    
    cache_stats = None
    if cache_manager:
        cache_stats = cache_manager.get_stats()
    
    config_info = {
        "version": server_config.version,
        "cache_ttl": server_config.cache_ttl,
        "request_timeout": server_config.request_timeout
    }
    
    return StatsResponse(
        cache_stats=cache_stats,
        server_stats=server_stats,
        config_info=config_info
    )


@app.post("/cache/clear", operation_id="clear_cache")
async def clear_cache() -> BaseResponse:
    """
    清空服务器缓存
    
    清除所有缓存数据，强制重新获取最新信息
    """
    if cache_manager:
        await cache_manager.clear()
        return BaseResponse(message="缓存已清空")
    else:
        return BaseResponse(message="缓存未启用")


@app.get("/schools", operation_id="get_supported_schools")
async def get_supported_schools() -> Dict[str, List[str]]:
    """
    获取支持的门派列表
    
    返回所有支持查询的门派名称
    """
    return {
        "schools": list(api_endpoints.school_mapping.keys()),
        "total": len(api_endpoints.school_mapping)
    }


@app.get("/health", operation_id="health_check")
async def health_check() -> BaseResponse:
    """
    健康检查接口
    
    用于检查MCP服务器运行状态
    """
    return BaseResponse(
        message="MCP服务器运行正常",
        server_stats={
            "status": "healthy",
            "service": server_config.mcp_name,
            "version": server_config.version,
            "cache_enabled": server_config.enable_cache
        }
    )


# 自定义异常处理
@app.exception_handler(HTTPException)
async def http_exception_handler(request: Request, exc: HTTPException):
    """HTTP异常处理器"""
    return JSONResponse(
        status_code=exc.status_code,
        content={
            "success": False,
            "message": exc.detail,
            "timestamp": time.time(),
            "path": str(request.url)
        }
    )


@app.exception_handler(Exception)
async def general_exception_handler(request: Request, exc: Exception):
    """通用异常处理器"""
    return JSONResponse(
        status_code=500,
        content={
            "success": False,
            "message": "服务器内部错误",
            "timestamp": time.time(),
            "path": str(request.url)
        }
    )

mcp.setup_server()
# 应用启动配置
if __name__ == "__main__":
    import uvicorn
    
    print("=" * 60)
    print("🎮 剑网3装备数据MCP服务器 v" + server_config.version)
    print("=" * 60)
    
    # 启动配置
    uvicorn_config = {
        "app": "my-MCP:app",
        "host": server_config.host,
        "port": server_config.port,
        "reload": server_config.reload,
        "access_log": server_config.access_log,
        "log_level": server_config.log_level
    }
    
    print(f"🚀 启动参数: {uvicorn_config}")
    print("📋 功能特性:")
    print("   ✅ 装备、套装、饰品查询")
    print("   ✅ 门派装备推荐")
    print("   ✅ 热门装备统计")
    print("   ✅ 缓存系统")
    print("   ✅ 请求统计")
    print("   ✅ 健康监控")
    print("\n按 Ctrl+C 停止服务器")
    
    uvicorn.run(**uvicorn_config)